from sqlalchemy.orm import Session
import logging
from typing import List, Optional, Dict, Any
from sqlalchemy import func, and_, or_
from fastapi import HTTPException
from app.database.models import Directory, File
from datetime import datetime

# 配置日志记录器
logger = logging.getLogger(__name__)

def create_directory(db: Session, user_id: int, name: str, parent_id: int = 0) -> Directory:
    """创建目录
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        name: 目录名称
        parent_id: 父目录ID，0表示根目录
        
    Returns:
        Directory: 创建的目录对象
    """
    # 验证目录名不为空
    if not name or not name.strip():
        raise HTTPException(status_code=400, detail="目录名不能为空")
    
    # 检查父目录是否存在（如果不是根目录）
    parent_path = ""
    if parent_id != 0:
        parent_directory = get_directory_by_id(db, parent_id, user_id)
        if not parent_directory:
            raise HTTPException(status_code=404, detail="父目录不存在")
        
        if parent_directory.is_deleted:
            raise HTTPException(status_code=400, detail="无法在已删除的目录中创建子目录")
            
        parent_path = parent_directory.path

    # 检查同级目录下是否已存在同名目录
    existing = db.query(Directory).filter(
        Directory.user_id == user_id,
        Directory.parent_id == parent_id,
        Directory.name == name,
        Directory.is_deleted == False
    ).first()
    
    if existing:
        raise HTTPException(status_code=400, detail=f"同级目录下已存在名为 '{name}' 的目录")
    
    # 生成目录路径
    directory_path = f"{parent_path}/{name}"
    
    # 创建目录记录
    directory = Directory(
        name=name,
        user_id=user_id,
        parent_id=parent_id,
        path=directory_path,
        is_deleted=False
    )
    
    db.add(directory)
    db.commit()
    db.refresh(directory)
    
    logger.info(f"用户 {user_id} 创建目录: {directory.path}, ID: {directory.id}")
    return directory

def get_directories(db: Session, user_id: int, parent_id: int = 0, include_deleted: bool = False) -> List[Directory]:
    """获取指定用户的目录列表
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        parent_id: 父目录ID，0表示获取根目录下的目录
        include_deleted: 是否包含已删除的目录
        
    Returns:
        List[Directory]: 目录列表
    """
    query = db.query(Directory).filter(Directory.user_id == user_id)
    
    query = query.filter(Directory.parent_id == parent_id)
    
    if not include_deleted:
        query = query.filter(Directory.is_deleted == False)
    
    return query.order_by(Directory.name).all()

def get_directory_by_id(db: Session, directory_id: int, user_id: int) -> Optional[Directory]:
    """根据ID获取目录
    
    Args:
        db: 数据库会话
        directory_id: 目录ID
        user_id: 用户ID
        
    Returns:
        Optional[Directory]: 查找到的目录，不存在则返回None
    """
    return db.query(Directory).filter(
        Directory.id == directory_id,
        Directory.user_id == user_id
    ).first()

def update_directory(db: Session, directory_id: int, user_id: int, name: Optional[str] = None, parent_id: Optional[int] = None) -> Directory:
    """更新目录信息
    
    Args:
        db: 数据库会话
        directory_id: 目录ID
        user_id: 用户ID
        name: 新目录名
        parent_id: 新的父目录ID，0表示移动到根目录
        
    Returns:
        Directory: 更新后的目录对象
    """
    directory = get_directory_by_id(db, directory_id, user_id)
    if not directory:
        raise HTTPException(status_code=404, detail="目录不存在")
    
    if directory.is_deleted:
        raise HTTPException(status_code=400, detail="已删除的目录不能修改")
    
    # 记录是否需要更新路径
    path_needs_update = False
    
    # 更新目录名称
    if name is not None and name.strip() and name != directory.name:
        # 检查同级目录下是否有同名目录
        existing = db.query(Directory).filter(
            Directory.user_id == user_id,
            Directory.parent_id == directory.parent_id,
            Directory.name == name,
            Directory.id != directory_id,
            Directory.is_deleted == False
        ).first()
        
        if existing:
            raise HTTPException(status_code=400, detail=f"同级目录下已存在名为 '{name}' 的目录")
        
        old_name = directory.name
        directory.name = name
        path_needs_update = True  # 标记需要更新路径
    
    # 更新父目录
    if parent_id is not None and parent_id != directory.parent_id:
        # 不能将目录移动到自己或子目录下
        if parent_id != 0 and is_subdirectory(db, directory_id, parent_id):
            raise HTTPException(status_code=400, detail="不能将目录移动到其子目录下")
        
        # 检查父目录是否存在
        if parent_id != 0:
            parent_directory = get_directory_by_id(db, parent_id, user_id)
            if not parent_directory:
                raise HTTPException(status_code=404, detail="父目录不存在")
            
            if parent_directory.is_deleted:
                raise HTTPException(status_code=400, detail="不能移动到已删除的目录")
                
            # 更新父目录ID和路径
            directory.parent_id = parent_id
            path_needs_update = True  # 标记需要更新路径
        else:
            # 移动到根目录
            directory.parent_id = 0
            path_needs_update = True  # 标记需要更新路径
    
    # 如果目录名称或父目录发生变化，则更新路径
    if path_needs_update:
        update_directory_path(db, directory)
    
    # 提交更改
    db.commit()
    db.refresh(directory)
    
    logger.info(f"用户 {user_id} 更新目录: {directory.path}, ID: {directory.id}")
    return directory

def delete_directory(db: Session, directory_id: int, user_id: int, permanent: bool = False) -> bool:
    """删除目录
    
    Args:
        db: 数据库会话
        directory_id: 目录ID
        user_id: 用户ID
        permanent: 是否永久删除，True表示物理删除，False表示逻辑删除（移到回收站）
        
    Returns:
        bool: 操作是否成功
    """
    directory = get_directory_by_id(db, directory_id, user_id)
    if not directory:
        raise HTTPException(status_code=404, detail="目录不存在")
    
    if permanent:
        # 递归删除所有子目录和文件
        _delete_subdirectories_recursive(db, directory_id, user_id, permanent=True)
        
        # 删除目录中的文件
        files = db.query(File).filter(File.directory_id == directory_id).all()
        for file in files:
            db.delete(file)
        
        # 删除目录本身
        db.delete(directory)
    else:
        # 逻辑删除，标记目录及其子目录和文件为已删除
        directory.is_deleted = True
        
        # 逻辑删除子目录
        _delete_subdirectories_recursive(db, directory_id, user_id, permanent=False)
        
        # 逻辑删除目录中的文件
        db.query(File).filter(
            File.directory_id == directory_id,
            File.user_id == user_id,
            File.is_deleted == False
        ).update({"is_deleted": True})
    
    db.commit()
    logger.info(f"用户 {user_id} {'永久' if permanent else '逻辑'}删除目录: {directory.path}, ID: {directory.id}")
    return True

def _delete_subdirectories_recursive(db: Session, parent_id: int, user_id: int, permanent: bool):
    """递归删除子目录
    
    Args:
        db: 数据库会话
        parent_id: 父目录ID
        user_id: 用户ID
        permanent: 是否永久删除
    """
    subdirs = db.query(Directory).filter(
        Directory.parent_id == parent_id,
        Directory.user_id == user_id
    ).all()
    
    for subdir in subdirs:
        # 递归处理子目录的子目录
        _delete_subdirectories_recursive(db, subdir.id, user_id, permanent)
        
        # 处理目录中的文件
        if permanent:
            files = db.query(File).filter(File.directory_id == subdir.id).all()
            for file in files:
                db.delete(file)
            db.delete(subdir)
        else:
            subdir.is_deleted = True
            db.query(File).filter(
                File.directory_id == subdir.id,
                File.user_id == user_id,
                File.is_deleted == False
            ).update({"is_deleted": True})

def is_subdirectory(db: Session, parent_id: int, child_id: int) -> bool:
    """检查一个目录是否是另一个目录的子目录（包括直接子目录和间接子目录）
    
    Args:
        db: 数据库会话
        parent_id: 父目录ID
        child_id: 子目录ID
        
    Returns:
        bool: 如果child_id是parent_id的子目录，则返回True
    """
    if parent_id == child_id:
        return True
    
    directory = db.query(Directory).filter(Directory.id == child_id).first()
    if not directory or directory.parent_id == 0:  # 修改：检查parent_id是否为0
        return False
    
    return is_subdirectory(db, parent_id, directory.parent_id)

def update_directory_path(db: Session, directory: Directory):
    """更新目录的完整路径
    
    Args:
        db: 数据库会话
        directory: 需要更新路径的目录对象
    """
    if directory.parent_id == 0:  # 修改：使用parent_id == 0检测根目录
        directory.path = f"/{directory.name}"
    else:
        parent = db.query(Directory).filter(Directory.id == directory.parent_id).first()
        if parent:
            directory.path = (parent.path or f"/{parent.name}") + f"/{directory.name}"
    
    # 更新所有子目录的路径
    _update_subdirectory_paths_recursive(db, directory)

def _update_subdirectory_paths_recursive(db: Session, parent_directory: Directory):
    """递归更新子目录的路径
    
    Args:
        db: 数据库会话
        parent_directory: 父目录对象
    """
    subdirs = db.query(Directory).filter(Directory.parent_id == parent_directory.id).all()
    for subdir in subdirs:
        subdir.path = parent_directory.path + f"/{subdir.name}"
        db.add(subdir)
        _update_subdirectory_paths_recursive(db, subdir)

def get_directory_tree(db: Session, user_id: int) -> List[Dict[str, Any]]:
    """获取用户的目录树结构
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        
    Returns:
        List[Dict[str, Any]]: 目录树结构
    """
    # 获取所有目录
    directories = db.query(Directory).filter(
        Directory.user_id == user_id,
        Directory.is_deleted == False
    ).all()
    
    # 获取每个目录下的文件数量
    dir_file_counts = {}
    file_counts = db.query(
        File.directory_id, 
        func.count(File.id).label('count')
    ).filter(
        File.user_id == user_id,
        File.is_deleted == False,
        File.directory_id.isnot(None)
    ).group_by(File.directory_id).all()
    
    for dir_id, count in file_counts:
        dir_file_counts[dir_id] = count
    
    # 创建目录ID到目录对象的映射
    dir_map = {d.id: {
        "id": d.id,
        "name": d.name,
        "path": d.path,
        "parent_id": d.parent_id,
        "children": [],
        "file_count": dir_file_counts.get(d.id, 0)
    } for d in directories}
    
    # 构建树结构
    root_dirs = []
    for d in directories:
        if d.parent_id == 0:  # 修改：使用parent_id == 0检测根目录
            # 根目录
            root_dirs.append(dir_map[d.id])
        elif d.parent_id in dir_map:
            # 添加到父目录的children列表中
            dir_map[d.parent_id]["children"].append(dir_map[d.id])
    
    return root_dirs

def move_files_to_directory(db: Session, user_id: int, file_ids: List[int], directory_id: int = 0) -> Dict[str, Any]:
    """将文件移动到指定目录
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        file_ids: 文件ID列表
        directory_id: 目标目录ID，0表示移动到根目录
        
    Returns:
        Dict[str, Any]: 操作结果统计
    """
    # 验证目录是否存在
    if directory_id != 0:
        directory = get_directory_by_id(db, directory_id, user_id)
        if not directory:
            raise HTTPException(status_code=404, detail="目标目录不存在")
        
        if directory.is_deleted:
            raise HTTPException(status_code=400, detail="不能移动文件到已删除的目录")
    
    # 移动文件
    success_count = 0
    failed_count = 0
    failed_files = []
    
    for file_id in file_ids:
        try:
            file = db.query(File).filter(
                File.id == file_id,
                File.user_id == user_id,
                File.is_deleted == False
            ).first()
            
            if not file:
                failed_count += 1
                failed_files.append({"id": file_id, "reason": "文件不存在或无权访问"})
                continue
            
            file.directory_id = directory_id
            db.add(file)
            success_count += 1
            
        except Exception as e:
            logger.error(f"移动文件 {file_id} 到目录 {directory_id} 失败: {str(e)}")
            failed_count += 1
            failed_files.append({"id": file_id, "reason": str(e)})
    
    db.commit()
    
    logger.info(f"用户 {user_id} 移动 {success_count} 个文件到目录 {directory_id}, {failed_count} 个失败")
    
    return {
        "success_count": success_count,
        "failed_count": failed_count,
        "failed_files": failed_files
    }

def get_root_files(db: Session, user_id: int) -> List[File]:
    """获取根目录下的文件（即未分配到任何目录的文件）
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        
    Returns:
        List[File]: 文件列表
    """
    return db.query(File).filter(
        File.user_id == user_id,
        File.directory_id == 0,
        File.is_deleted == False
    ).all()

def get_files_in_directory(db: Session, user_id: int, directory_id: int) -> List[File]:
    """获取指定目录下的文件
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        directory_id: 目录ID
        
    Returns:
        List[File]: 文件列表
    """
    return db.query(File).filter(
        File.user_id == user_id,
        File.directory_id == directory_id,
        File.is_deleted == False
    ).all()

def move_directories_to_directory(db: Session, user_id: int, directory_ids: List[int], target_directory_id: int = 0) -> Dict[str, Any]:
    """将目录移动到指定目录下
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        directory_ids: 要移动的目录ID列表
        target_directory_id: 目标父目录ID，0表示移动到根目录
        
    Returns:
        Dict[str, Any]: 操作结果统计
    """
    # 验证目标目录是否存在
    if target_directory_id != 0:
        target_directory = get_directory_by_id(db, target_directory_id, user_id)
        if not target_directory:
            raise HTTPException(status_code=404, detail="目标目录不存在")
        
        if target_directory.is_deleted:
            raise HTTPException(status_code=400, detail="不能移动到已删除的目录")
    
    # 移动目录
    success_count = 0
    failed_count = 0
    failed_directories = []
    
    for directory_id in directory_ids:
        try:
            # 检查目录是否存在
            directory = get_directory_by_id(db, directory_id, user_id)
            if not directory:
                failed_count += 1
                failed_directories.append({"id": directory_id, "reason": "目录不存在或无权访问"})
                continue
            
            # 不能将目录移动到自己或子目录下
            if target_directory_id != 0 and (directory_id == target_directory_id or is_subdirectory(db, directory_id, target_directory_id)):
                failed_count += 1
                failed_directories.append({"id": directory_id, "reason": "不能将目录移动到自己或其子目录下"})
                continue
            
            # 检查目标目录下是否有同名目录
            same_name_directory = db.query(Directory).filter(
                Directory.user_id == user_id,
                Directory.parent_id == target_directory_id,
                Directory.name == directory.name,
                Directory.id != directory_id,
                Directory.is_deleted == False
            ).first()
            
            if same_name_directory:
                failed_count += 1
                failed_directories.append({"id": directory_id, "reason": f"目标目录下已存在名为 '{directory.name}' 的目录"})
                continue
            
            # 执行移动
            directory.parent_id = target_directory_id
            
            # 更新目录路径
            update_directory_path(db, directory)
            
            success_count += 1
            
        except Exception as e:
            logger.error(f"移动目录 {directory_id} 到目录 {target_directory_id} 失败: {str(e)}")
            failed_count += 1
            failed_directories.append({"id": directory_id, "reason": str(e)})
    
    db.commit()
    
    logger.info(f"用户 {user_id} 移动 {success_count} 个目录到目录 {target_directory_id}, {failed_count} 个失败")
    
    return {
        "success_count": success_count,
        "failed_count": failed_count,
        "failed_directories": failed_directories
    }

def move_items_to_directory(db: Session, user_id: int, file_ids: List[int], directory_ids: List[int], target_directory_id: int = 0) -> Dict[str, Any]:
    """将文件和目录移动到指定目录
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        file_ids: 要移动的文件ID列表
        directory_ids: 要移动的目录ID列表
        target_directory_id: 目标目录ID，0表示移动到根目录
        
    Returns:
        Dict[str, Any]: 操作结果统计
    """
    # 移动文件
    file_result = move_files_to_directory(db, user_id, file_ids, target_directory_id) if file_ids else {"success_count": 0, "failed_count": 0, "failed_files": []}
    
    # 移动目录
    dir_result = move_directories_to_directory(db, user_id, directory_ids, target_directory_id) if directory_ids else {"success_count": 0, "failed_count": 0, "failed_directories": []}
    
    # 合并结果
    return {
        "files": {
            "success_count": file_result["success_count"],
            "failed_count": file_result["failed_count"],
            "failed_files": file_result["failed_files"]
        },
        "directories": {
            "success_count": dir_result["success_count"],
            "failed_count": dir_result["failed_count"],
            "failed_directories": dir_result["failed_directories"]
        },
        "total_success_count": file_result["success_count"] + dir_result["success_count"],
        "total_failed_count": file_result["failed_count"] + dir_result["failed_count"]
    }

def batch_delete_items(db: Session, user_id: int, file_ids: List[int], directory_ids: List[int], permanent: bool = False, recursive: bool = True) -> Dict[str, Any]:
    """批量删除文件和目录
    
    Args:
        db: 数据库会话
        user_id: 用户ID
        file_ids: 要删除的文件ID列表
        directory_ids: 要删除的目录ID列表
        permanent: 是否永久删除，True表示物理删除，False表示逻辑删除（移到回收站）
        recursive: 删除目录时是否递归删除子目录和文件
        
    Returns:
        Dict[str, Any]: 删除结果统计
    """
    from app.services.file_service import batch_delete_files
    
    logger.info(f"批量删除项目: user_id={user_id}, directory_ids={directory_ids}, file_ids={file_ids}, permanent={permanent}, recursive={recursive}")
    
    results = {
        "directories": {
            "success_count": 0,
            "failed_count": 0,
            "failed_directories": [],
            "affected_files_count": 0
        },
        "files": {
            "success_count": 0,
            "failed_count": 0,
            "failed_files": []
        },
        "total_success_count": 0,
        "total_failed_count": 0,
        "is_permanent": permanent
    }
    
    # 处理目录删除
    affected_files_count = 0
    for directory_id in directory_ids:
        try:
            directory = get_directory_by_id(db, directory_id, user_id)
            if not directory:
                logger.warning(f"目录不存在或无权访问: directory_id={directory_id}, user_id={user_id}")
                results["directories"]["failed_count"] += 1
                results["directories"]["failed_directories"].append({
                    "directory_id": directory_id,
                    "reason": "目录不存在或无权访问"
                })
                continue
            
            # 统计受影响的文件数量
            affected_files = db.query(File).filter(
                File.user_id == user_id,
                File.directory_id == directory_id,
                File.is_deleted == False
            ).count()
            
            # 执行目录删除
            delete_directory(db, directory_id, user_id, permanent)
            
            results["directories"]["success_count"] += 1
            results["directories"]["affected_files_count"] += affected_files
            logger.info(f"成功删除目录: directory_id={directory_id}")
            
        except Exception as e:
            logger.error(f"删除目录出错: directory_id={directory_id}, error={str(e)}")
            results["directories"]["failed_count"] += 1
            results["directories"]["failed_directories"].append({
                "directory_id": directory_id,
                "reason": str(e)
            })
    
    # 处理文件删除
    if file_ids:
        file_results = batch_delete_files(db, file_ids, user_id, permanent)
        results["files"]["success_count"] = file_results["success_count"]
        results["files"]["failed_count"] = file_results["failed_count"]
        results["files"]["failed_files"] = file_results["failed_files"]
    
    # 更新总计数据
    results["total_success_count"] = results["directories"]["success_count"] + results["files"]["success_count"]
    results["total_failed_count"] = results["directories"]["failed_count"] + results["files"]["failed_count"]
    
    return results 